﻿using System;
using System.Collections;
using System.Collections.Generic;
using Actors;
using Common;
using GameCamera;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace GameScene
{
    #region Foucs Object

    public interface IFocusable
    {
        GameObject GameObject { get; }
        void OnFocus();
        void OnLostFocus();
    }

    #endregion
    
    
    public class GameSceneManager : Singleton<GameSceneManager>
    {

        #region Members

        /// <summary>
        /// 玩家对象容器
        /// </summary>
        private readonly ActorContainer m_playerContainer = new ActorContainer();

        private LocalPlayer m_LocalPlayer;
        public LocalPlayer LocalPlayer
        {
            get
            {
                return m_LocalPlayer;
            }
            set
            {
                m_LocalPlayer = value;
            }
        }

        /// <summary>
        /// 当前聚焦的游戏对象
        /// </summary>
        public IFocusable CurrentFocus { get; private set; }
        
        private Camera m_MainCamera;

        #endregion
        
        public void Initialize()
        {
            m_playerContainer.Initialize("Players");
        }
        
        public void PostInitialize()
        {
            m_MainCamera = CameraManager.Instance.mainCamera;
        }
        
        public void Update()
        {
            try
            {
                m_playerContainer.Update();
            }
            catch (Exception e)
            {
#if UNITY_EDITOR
                Debug.LogException(e);
#endif
            }
        }

        public void FixedUpdate()
        {
            try
            {
                m_playerContainer.FixedUpdate();
                UpdateFocusObject();
            }
            catch (Exception e)
            {
#if UNITY_EDITOR
                Debug.LogException(e);
#endif
            }

        }

        #region Actors
        
        private GameObject SpawnGameObject()
        {
            var go = new GameObject();
            return go;
        }
        
        private T SpawnActor<T>(ActorInitParams initParams) where T : Actor, new()
        {
            var actor = new T();
            actor.Initialize(initParams);
            m_playerContainer.AddActor(actor);
            return actor;
        }
        
        public LocalPlayer SpawnLocalPlayer(ActorInitParams initParams)
        {
            var localPlayer = SpawnActor<LocalPlayer>(initParams);
            m_LocalPlayer = localPlayer;
            return localPlayer;
        }
        
        #endregion


        #region Focus Object

        private void UpdateFocusObject()
        {
            if (m_MainCamera == null)
            {
                return;
            }
            var ray = m_MainCamera.ScreenPointToRay(Vector3.zero);
            if (Physics.Raycast(ray, out RaycastHit hitInfo, 10, LayerUtility.Visible))
            {
                if (CurrentFocus != null)
                {
                    if (CurrentFocus.GameObject == hitInfo.collider.gameObject) return;
                    CurrentFocus.OnLostFocus();
                    CurrentFocus = hitInfo.collider.GetComponent<IFocusable>();
                    CurrentFocus?.OnFocus();
                }
                else
                {
                    CurrentFocus = hitInfo.collider.GetComponent<IFocusable>();
                    CurrentFocus?.OnFocus();
                }
            }
            else
            {
                if (CurrentFocus == null) return;
                CurrentFocus.OnLostFocus();
                CurrentFocus = null;
            }
 
        }

        #endregion

        #region SceneLoad

        public void LoadScene(string sceneName)
        {
            GameRoot.CoroutineRoot.StartCoroutine(LoadSceneAsync(sceneName));
        }

        private IEnumerator LoadSceneAsync(string sceneName)
        {
            AsyncOperation asyncOperation = SceneManager.LoadSceneAsync(sceneName);
            asyncOperation.allowSceneActivation = false;

            while (!asyncOperation.isDone)
            {

                // Check if the load has finished
                if (asyncOperation.progress >= 0.9f)
                {
                    // Activate the Scene
                    asyncOperation.allowSceneActivation = true;
                }

                yield return null;
            }
        }

        #endregion
    }


}